<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>7.3.1-自定义渲染器</title>
</head>

<body>
    <div id="app"></div>
</body>
    <script src="https://unpkg.com/@vue/reactivity@3.0.5/dist/reactivity.global.js"></script>
    <script>
        /**
         * 
         * 通过将渲染器设计为可配置的 “通用” 渲染器，即可实现渲染到任意目标平台上
         * - 以浏览器作为渲染的目标平台，编写一个渲染器
         * - 将浏览器特定的 API 抽离，这样就可以使得渲染器的核心不依赖于浏览器
         * - 再为那些被抽离的 API 提供可配置的接口，即可实现渲染器的跨平台能力。
         * 
         * vnode 对象
         * - 使用 type 属性来描述一个 vnode 的类型，不同类型的 type 属性值可以描述多种类型的 vnode
         * - 当 type 属性是字符串类型值时，可以认为它描述的是普通标签，
         *    并使用该 type 属性的字符串值作为标签的名称
         * 
         * 完成了一个 vnode 的挂载
         * - 首先调用 document.createElement 函数，
         *    以 vnode.type 的值作为标签名称创建新的 DOM 元素
         * - 接着处理 vnode.children，
         *    如果它的值是字符串类型，则代表该元素具有文本子节点，这时只需要设置元素的 textContent 即可
         * - 最后调用 appendChild 函数将新创建的 DOM 元素添加到容器元素内
         * 
         * */ 
        
        function createRenderer() {
            
            function mountElement(vnode, container) { // 新增
                // 创建 DOM 元素
                const el = document.createElement(vnode.type)

                // 处理子节点，如果子节点是字符串，代表元素具有文本节点
                if (typeof vnode.children === 'string') {
                    // 因此只需要设置元素的 textContent 属性即可
                    el.textContent = vnode.children
                }

                // 将元素添加到容器中
                container.appendChild(el)
            }

            // 第一个参数 n1 代表旧 vnode，第二个参数 n2 代表新 vnode。
            function patch(n1, n2, container) { // 新增
                // 如果 n1 不存在，意味着挂载，则调用 mountElement 函数完成挂载
                if (!n1) {
                    mountElement(n2, container)
                } else {
                    // n1 存在，意味着打补丁，暂时省略
                }
            }

            function render(vnode, container) {
                if (vnode) {
                    // 新 vnode 存在，将其与旧 vnode 一起传递给 patch 函数，进行打补丁
                    patch(container._vnode, vnode, container)
                } else {
                    if (container._vnode) {
                        // 旧 vnode 存在，且新 vnode 不存在，说明是卸载（unmount）操作
                        // 只需要将 container 内的 DOM 清空即可
                        container.innerHTML = ''
                    }
                }
                // 把 vnode 存储到 container._vnode 下，即后续渲染中的旧 vnode
                container._vnode = vnode
            }

            return {
                render
            }
        }

        // 创建一个渲染器
        const renderer = createRenderer()

        // 新增 测试
        // vnode 对象来描述一个 <h1> 标签
            const vnode = {
            type: 'h1',
            children: 'hello 11'
        }

        // 调用 render 函数渲染该 vnode
        renderer.render(vnode, document.querySelector('#app'))




    </script>
    

</html>